<?php


namespace App\Controller\Api;

use _HumbugBox373c0874430e\Nette\Neon\Exception;
use App\Constants\ErrorForPayApi;
use App\Middleware\Api\PayApiAuthMiddleware;

// use App\Middleware\MchApi\MchJWTAuthMiddleware;
use App\Middleware\Api\PayServerSignMiddleware;
use App\Middleware\Api\PayWebSignMiddleware;
use App\Middleware\TranslatorMiddleware;
use App\Model\Enum\PayTypeEnum;
use App\Model\Enum\StatusEnum;
use App\Service\MchPayService;
use App\Service\SysCardTypeInfoService;
use App\Utils\CreditCardUtils;
use App\Utils\CustomUtils;
use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpServer\Annotation\Controller;
use Hyperf\HttpServer\Annotation\Middleware;
use Hyperf\HttpServer\Annotation\Middlewares;
use Hyperf\HttpServer\Annotation\PostMapping;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\HttpServer\Contract\ResponseInterface;
use Hyperf\Utils\Arr;

/**
 * Class PayController
 * 支付
 *
 * @package App\Controller\Api
 * @Controller(prefix="/api/pay")
 * @Middleware(TranslatorMiddleware::class)
 */
class PayController extends BaseController {

    // TODO::redirect() 跳转
    // TODO::callback() 支付回调跳转
    // TODO::callbackRedirect() 支付回调跳转
    // TODO::orderQuery() 查询支付订单
    // TODO::refund() 退款
    // TODO::transfer() 转帐

    /**
     * @var MchPayService
     * @Inject()
     */
    protected $MchPayService;

    /**
     * @var SysCardTypeInfoService
     * @Inject()
     */
    protected $SysCardTypeInfoService;

    /**
     * 服务器对接签名测试
     * @PostMapping(path="testServerSign")
     * @Middlewares({
     *     @Middleware(PayApiAuthMiddleware::class),
     *     @Middleware(PayServerSignMiddleware::class),
     * })
     *
     * @param RequestInterface $request
     *
     * @return array
     */
    public function testServerSign (RequestInterface $request) {

        $data = [
            'param' => $request->post(),
        ];

        return CustomUtils::buildSuccess($data, trans('pay.sign.verify.success'));
    }

    /**
     * 用户端对接签名测试
     * @PostMapping(path="testWebSign")
     * @Middlewares({
     *     @Middleware(PayApiAuthMiddleware::class),
     *     @Middleware(PayWebSignMiddleware::class),
     * })
     *
     * @param RequestInterface $request
     *
     * @return array
     */
    public function testWebSign (RequestInterface $request) {

        $data = [
            'param' => $request->post(),
        ];

        return CustomUtils::buildSuccess($data, trans('pay.sign.verify.success'));
    }

    /**
     * 获取商户支付配置信息
     * @PostMapping(path="getMchPayInfo")
     * @Middleware(PayApiAuthMiddleware::class)
     *
     * @param RequestInterface $request
     *
     * @return array
     */
    public function getMchPayInfo (RequestInterface $request) {

        $ext = $request->getAttribute('ext', []);

        $param = [
            'mchInfo'   => Arr::get($ext, 'mchInfo'),
            'mchConfig' => Arr::get($ext, 'mchConfig'),
        ];

        return $this->MchPayService->getMchPayInfo($param);
    }

    /**
     * 用户支付
     * @PostMapping(path="userPay")
     * @Middlewares({
     *     @Middleware(PayApiAuthMiddleware::class),
     *     @Middleware(PayWebSignMiddleware::class),
     * })
     *
     * @param RequestInterface $request
     *
     * @return array
     * @throws \Exception
     */
    public function userPay (RequestInterface $request) {

        $this->_validOrFail($request->all(), [
            'orderNo'          => 'sometimes', // 商户单号
            'mchTransAmount'   => 'required', // 交易金额(分)
            'mchTransCurrency' => 'required', // 交易币种
            'payType'          => 'required', // 支付类型
        ]);

        $params = [
            'mch'         => $this->_getParamMch($request),
            'order'       => $this->_getParamOrder($request),
            'payType'     => $request->post('payType'),
            'payAccount'  => $this->_getParamPayAccount($request),
            'environment' => array_merge($this->_getParamEnvironment($request), [
                'ipAddress' => CustomUtils::xzxGetClientIp(),
            ]),
            'after'       => $this->_getParamAfter($request),
        ];

        return $this->MchPayService->userPay($params);
    }

    /**
     * 直连支付网关
     * @PostMapping(path="pay")
     * @Middlewares({
     *     @Middleware(PayApiAuthMiddleware::class),
     *     @Middleware(PayServerSignMiddleware::class),
     * })
     *
     * @param RequestInterface $request
     *
     * @return array
     * @throws \Throwable
     */
    public function pay (RequestInterface $request) {

        // 这里的参数大体分为: 验签相关, 订单相关, 用户支付信息, 客户端环境
        // 验签判断数据能否信任,商户有无权限
        // 订单包含了交易金额和商户单号,以及回调/跳转/商品信息
        // 支付信息根据支付类型, 有不同的参数列表, 初步支持信用卡/支付宝/微信, 具体要看各种支付所需参数
        // 客户端环境是用户的IP/支付的地址/agent/协议等信息, 通常用于风控

        $this->_validOrFail($request->all(), [
            'orderNo'          => 'required', // 商户单号
            'mchTransAmount'   => 'required', // 交易金额(分)
            'mchTransCurrency' => 'required', // 交易币种
            'payType'          => 'required', // 支付类型
        ]);

        $params = [
            'mch'         => $this->_getParamMch($request),
            'order'       => $this->_getParamOrder($request),
            'payType'     => $request->post('payType'),
            'payAccount'  => $this->_getParamPayAccount($request),
            'environment' => $this->_getParamEnvironment($request),
            'after'       => $this->_getParamAfter($request),
        ];

        // var_dump($params);

        return $this->MchPayService->doPay($params);
    }

    /**
     * 支付中转网关
     * 提交数据创建预支付订单,返回支付页面地址让用户填写支付信息
     * @PostMapping(path="payJump")
     * @Middlewares({
     *     @Middleware(PayApiAuthMiddleware::class),
     *     @Middleware(PayServerSignMiddleware::class),
     * })
     *
     * @param RequestInterface  $request
     * @param ResponseInterface $response
     *
     * @return array
     * @throws \Throwable
     */
    public function payJump (RequestInterface $request, ResponseInterface $response) {

        $this->_validOrFail($request->all(), [
            'orderNo'          => 'required', // 商户单号
            'mchTransAmount'   => 'required', // 交易金额(分)
            'mchTransCurrency' => 'required', // 交易币种
        ]);

        $params = [
            'mch'         => $this->_getParamMch($request),
            'order'       => $this->_getParamOrder($request),
            'payType'     => null,
            'payAccount'  => null,
            'environment' => $this->_getParamEnvironment($request),
            'after'       => $this->_getParamAfter($request),
        ];

        return $this->MchPayService->doPayJump($params);
    }

    /**
     * 支付页面填写信息后提交支付
     * @PostMapping(path="payJumpConfirm")
     * @Middlewares({
     *     @Middleware(PayApiAuthMiddleware::class),
     *     @Middleware(PayServerSignMiddleware::class),
     * })
     *
     * @param RequestInterface  $request
     * @param ResponseInterface $response
     *
     * @return array
     * @throws \Exception
     */
    public function payJumpConfirm (RequestInterface $request, ResponseInterface $response) {

        $this->_validOrFail($request->all(), [
            'payType'          => 'required', // 商户单号
            'tradeNo'          => 'required', // 商户单号
            'mchTransAmount'   => 'required', // 交易金额(分)
            'mchTransCurrency' => 'required', // 交易币种
        ]);

        $params = [
            'mch'         => $this->_getParamMch($request),
            'order'       => $this->_getParamOrder($request, true),
            'payType'     => $request->post('payType'),
            'payAccount'  => $this->_getParamPayAccount($request),
            'environment' => $this->_getParamEnvironment($request),
            'after'       => $this->_getParamAfter($request),
        ];

        return $this->MchPayService->doPayJumpConfirm($params);
    }

    /**
     * 信用卡信息校验
     * @PostMapping(path="verifyCreditCard")
     * @Middleware(PayApiAuthMiddleware::class)
     *
     * @param RequestInterface $request
     *
     * @return array
     * @throws \Throwable
     */
    public function verifyCreditCard (RequestInterface $request) {

        $this->_validOrFail($request->all(), [
            // 卡号
            'cardNo' => 'required',
        ]);

        $param = [
            'cardNo'      => $request->post('cardNo'),
            'cardType'    => $request->post('cardType'),
            'cardCvv'     => $request->post('cardCvv'),
            'cardExpireY' => $request->post('cardExpireY'),
            'cardExpireM' => $request->post('cardExpireM'),
        ];

        return $this->MchPayService->verifyCreditCard($param);
    }

    /**
     * 后处理参数
     *
     * @param RequestInterface $request
     *
     * @return array
     */
    protected function _getParamAfter (RequestInterface $request) {

        $after = [
            'isPayJump'    => $request->post('isPayJump'),
            'returnUrl'    => $request->post('returnUrl', ''),
            'mchNotifyUrl' => $request->post('mchNotifyUrl', ''),
        ];

        return $after;
    }

    /**
     * 获取环境参数
     *
     * @param RequestInterface $request
     *
     * @return array
     */
    protected function _getParamEnvironment (RequestInterface $request) {

        // 支付环境
        $environment = [
            'httpProtocol' => $request->server('server_protocol'),
            'submitUrl'    => $request->getHeaderLine('host').$request->server('request_uri'),
            'payWebSite'   => $request->getHeaderLine('host').$request->server('request_uri'),
            'webInfo'      => $request->getHeaderLine('user-agent'),
            'ipAddress'    => $request->post('ipAddress', CustomUtils::xzxGetClientIp()),
        ];

        return $environment;
    }

    /**
     * 获取商户相关参数
     *
     * @param RequestInterface $request
     *
     * @return array
     */
    protected function _getParamMch (RequestInterface $request) {

        $ext = $request->getAttribute('ext', []);

        $mch = [
            'mchInfo'    => Arr::get($ext, 'mchInfo'),
            'mchConfig'  => Arr::get($ext, 'mchConfig'),
            'mchAccount' => Arr::get($ext, 'mchAccount'),
            'mchUrl'     => Arr::get($ext, 'mchUrl'),
        ];

        return $mch;
    }

    protected function _getParamOrder (RequestInterface $request, $byTradeNo = false) {

        if ($byTradeNo) {

            $order = [
                'tradeNo'          => $request->post('tradeNo'),
                'mchTransAmount'   => $request->post('mchTransAmount'),
                'mchTransCurrency' => $request->post('mchTransCurrency'),
            ];
        }
        else {

            $order = [
                'orderNo'          => $request->post('orderNo', ''),
                'mchTransAmount'   => $request->post('mchTransAmount'),
                'mchTransCurrency' => $request->post('mchTransCurrency'),


                // 额外信息,商品信息,收货信息
                'attach'           => $request->post('attach', ''),
                'goodsInfo'        => $request->post('goodsInfo', ''),

                'grCountry'     => $request->post('grCountry', ''),
                'grState'       => $request->post('grState', ''),
                'grCity'        => $request->post('grCity', ''),
                'grRegion'      => $request->post('grCountry', ''),
                'grAddress'     => $request->post('grAddress', ''),
                'grZipCode'     => $request->post('grZipCode', ''),
                'grEmail'       => $request->post('grEmail', ''),
                'grPhoneNumber' => $request->post('grPhoneNumber', ''),
                'grPerName'     => $request->post('grPerName', ''),
            ];
        }

        return $order;
    }

    /**
     * 从请求参数里构建支付参数
     *
     * @param RequestInterface $request
     *
     * @return array
     * @throws \Exception
     */
    protected function _getParamPayAccount (RequestInterface $request) {

        $payType = $request->post('payType');

        $payAccount = [];
        switch ($payType) {
            case PayTypeEnum::CREDIT:
                $payAccount = $this->_getParamPayAccountCredit($request);
                break;
            default:
                throw new \Exception(trans('pay.type.not_support'));
                break;
        }

        return $payAccount;
    }

    /**
     * 从请求参数里构建信用卡支付参数
     *
     * @param RequestInterface $request
     *
     * @return array
     * @throws \Exception
     */
    protected function _getParamPayAccountCredit (RequestInterface $request) {

        // 卡号卡种校验
        $cardNo   = $request->post('cardNo', '');
        $cardType = strtolower($request->post('cardType', ''));

        // 校验卡号合法性, 并通过卡号获取卡种
        $card = CreditCardUtils::validCreditCard($cardNo, $cardType);
        if (!$card['valid']) {
            throw new \Exception(trans('pay.account.credit.mismatched'));
        }
        $cardNo   = $card['number'];
        $cardType = $card['type'];

        // 校验cvv,年月
        $cardCvv     = $request->post('cardCvv');
        $cardExpireY = $request->post('cardExpireY');
        $cardExpireM = $request->post('cardExpireM');

        if (!CreditCardUtils::validCvc($cardCvv, $cardType)) {
            throw new \Exception(trans('pay.account.credit.cvv_fail'));
        }

        if (!CreditCardUtils::validDate($cardExpireY, $cardExpireM)) {
            throw new \Exception(trans('pay.account.credit.date_fail'));
        }

        // 卡种参数归一化
        $cardType = $this->SysCardTypeInfoService->formatCardTypeOrFail($cardType);

        // 信用卡支付信息
        $payAccount = [

            // 基础信息
            'cardNo'        => $cardNo,
            'cardType'      => $cardType,
            'cardCvv'       => $cardCvv,
            'cardExpireY'   => $cardExpireY,
            'cardExpireM'   => $cardExpireM,

            // 持卡人信息
            'cardCountry'   => $request->post('cardCountry', ''),
            'cardState'     => $request->post('cardState', ''),
            'cardCity'      => $request->post('cardCity', ''),
            'cardRegion'    => $request->post('cardCountry', ''),
            'cardAddress'   => $request->post('cardAddress', ''),
            'cardFullPhone' => $request->post('cardFullPhone', ''),
            'cardEmail'     => $request->post('cardEmail', ''),
            'cardFullName'  => $request->post('cardFullName', ''),
            'cardZipCode'   => $request->post('cardZipCode', ''),

            // 强制上送银行网关失败
            'forceBankFail' => (bool) $request->post('forceBankFail', false),
        ];

        return $payAccount;
    }

    /**
     * 平台支持卡种列表
     * @PostMapping(path="cardTypeOptions")
     * @Middleware(PayApiAuthMiddleware::class)
     *
     * @param RequestInterface $request
     *
     * @return array
     * @throws \Throwable
     */
    public function cardTypeOptions (RequestInterface $request) {
        $params = [
            'status' => StatusEnum::ON,
        ];
        return $this->SysCardTypeInfoService->getOption($params);
    }

    /**
     * 商户支付方式列表
     * @PostMapping(path="payTypeOptions")
     * @Middleware(PayApiAuthMiddleware::class)
     *
     * @param RequestInterface $request
     *
     * @return array
     * @throws \Throwable
     */
    public function payTypeOptions (RequestInterface $request) {
        $ext    = $request->getAttribute('ext', []);
        $params = [
            'mchInfo'    => Arr::get($ext, 'mchInfo'),
            'mchConfig'  => Arr::get($ext, 'mchConfig'),
            'mchAccount' => Arr::get($ext, 'mchAccount'),
        ];
        return $this->MchPayService->payTypeOptions($params);
    }

    /**
     * 商户支持币种列表
     * @PostMapping(path="currencyOptions")
     * @Middleware(PayApiAuthMiddleware::class)
     *
     * @param RequestInterface $request
     *
     * @return array
     * @throws \Throwable
     */
    public function currencyOptions (RequestInterface $request) {
        $ext    = $request->getAttribute('ext', []);
        $params = [
            'mchInfo'    => Arr::get($ext, 'mchInfo'),
            'mchConfig'  => Arr::get($ext, 'mchConfig'),
            'mchAccount' => Arr::get($ext, 'mchAccount'),
        ];
        return $this->MchPayService->currencyOptions($params);
    }


}
